package com.simple.middle.lock.redis;

import com.simple.middle.lock.SimpleLock;
import com.simple.middle.lock.util.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

/**
 * 项目: simple-lock
 *
 * 功能描述: redis实现的分布式锁
 *
 * @author: WuChengXing
 *
 * @create: 2023-04-29 00:31
 **/
@Component
public class Rlock implements SimpleLock {

    private final Logger log = LoggerFactory.getLogger("R-LOCK");
    private static final String SUCCESS = "OK";
    private static final String LUA_DEL_SCRIPT = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void lock() {

    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {

    }

    @Override
    public Condition newCondition() {
        return null;
    }

    @Override
    public List<String> currentLockKeys() {
        return null;
    }

    @Override
    public String tryLock(String lockKey, long expireSeconds) {
        String uuid = CommonUtils.getUuid();
        boolean result = tryLock(lockKey, uuid, expireSeconds);
        return result ? uuid : null;
    }

    @Override
    public boolean tryLock(String lockKey, String value, long expireSeconds) {
        try {
            String result = redisTemplate.execute((RedisCallback<String>) (connection) -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                // 该命令保证了原子性
                return commands.set(lockKey, value, "NX", "EX", expireSeconds);
            });
            return SUCCESS.equals(result);
        } catch (Exception var5) {
            log.error("获取锁出现服务异常，获取的key：{}, value: {}", lockKey, value);
            return false;
        }
    }

    @Override
    public String lock(String lockKey, long expireSeconds, int tryTimes, long sleepMillis) {
        String uuid = CommonUtils.getUuid();
        boolean result = lock(lockKey, uuid, expireSeconds, tryTimes, sleepMillis);
        return result ? uuid : null;
    }

    @Override
    public boolean lock(String lockKey, String lockValue, long expireSeconds, int tryTimes, long sleepMillis) {
        boolean result = false;
        int count = 0;
        try {
            do {
                ++count;
                result = tryLock(lockKey, lockValue, expireSeconds);
                // 没有拿到锁的时候进行重试
                if (!result) {
                    TimeUnit.MILLISECONDS.sleep(sleepMillis);
                }
            } while (!result && count <= tryTimes);
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }

        return result;
    }

    @Override
    public void unlock(String lockKey, String lockValue) {
        try {
            // 执行lua脚本进行原子删除
            RedisScript<Long> redisScript = new DefaultRedisScript<>(LUA_DEL_SCRIPT, Long.class);
            redisTemplate.execute(redisScript, Collections.singletonList(lockKey), lockValue);
        } catch (Exception var3) {
            log.error("unlock error:", var3);
        }
    }
}
